#ifdef _WIN32
#include "th.h"
#include <assert.h>
#include <time.h>
#include <stdio.h>
C_CODE_BEGIN
#define WIN32_THREAD_EXIT_CODE 0
#define WIN32_SPIN_TO_SLEEP_LOOPS 16
#define WIN32_PURE_SPINLOCK_LOOPS 128
/*
 * The thread callback function on windows is different with Posix pthread , in fact the return value of the
 * thread callback is meaningless in our framework. However for simplicity , I reserve this. On windows , things
 * changed , since the windows callback for _beginthreadex( CreateThread ) just return a unsigned int (exit-code).
 * I have to wrap the standard callback to adapt to this constraint. This wrapping make things very complicated ,
 * since a piece of dynamic memory will be passed to the callback function.
 */

struct Win32ThreadArgument {
	void* input;
	void* output;
	thread_cb cb;
	bool detach;
	HANDLE handler;
};

static unsigned int __stdcall win32_thread_cb( void* argument ) {
	struct Win32ThreadArgument* ar = (struct Win32ThreadArgument*)(argument);

	ar->output = ar->cb(ar->input);
	if( ar->detach ) {
		CloseHandle( ar->handler );
		free(ar);
	}
	return WIN32_THREAD_EXIT_CODE;
}


// More info about this function see:
// http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx
#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
	DWORD dwType; // Must be 0x1000.
	LPCSTR szName; // Pointer to name (in user addr space).
	DWORD dwThreadID; // Thread ID (-1=caller thread).
	DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
static void _thread_set_name( HANDLE handler , const char* name ) {
	char buf[512];
	DWORD dwId = GetThreadId( handler );
	sprintf( buf , "%s[thread_id:%d]",name,dwId );
	if( IsDebuggerPresent()  ) {
		THREADNAME_INFO info;
		info.dwType = 0x1000;
		info.szName = buf;
		info.dwThreadID = dwId;
		info.dwFlags = 0;
		__try {
			RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD),(DWORD_PTR*)(&info));
		} __except(EXCEPTION_CONTINUE_EXECUTION) {
		} 
	}
}


EXPORT_FUNC bool thread_create( thread_handler* handler , const char* name, thread_cb cb , void* par , bool detach ) {
	struct Win32ThreadArgument* pArg = malloc( sizeof(struct Win32ThreadArgument) );
	pArg->input = par;
	pArg->cb = cb;
	pArg->handler = (HANDLE) _beginthreadex( NULL , 0 , win32_thread_cb , pArg , 0 , NULL );
	if( pArg->handler == INVALID_HANDLE_VALUE ) {
		free(pArg);
		return false;
	}
	if( name != NULL )
		_thread_set_name(pArg->handler,name);
	pArg->detach = detach;
	*handler = (thread_handler)(pArg);
	return true;
}

EXPORT_FUNC bool thread_join( thread_handler handler , void** return_val , const struct th_join_sec * ti ) {
	struct Win32ThreadArgument* arg = ( struct Win32ThreadArgument *)(handler);
	DWORD dwRet;
	DWORD dwMSec;
	assert( !(arg->detach) );

	if( ti == NULL ) {
		dwMSec= INFINITE;
	} else {
		if( ti->sec == 0 && ti->micro_sec == 0 ) {
			dwMSec = 0;
		} else {
			dwMSec = ti->micro_sec/1000u + ti->sec*1000u;
		}
	}

	dwRet = WaitForSingleObject( arg->handler , dwMSec );

	switch( dwRet ) {
	case WAIT_TIMEOUT:
		return false;
	case WAIT_OBJECT_0:
		if( return_val ) {
			*return_val = arg->output;
		}
		free(arg);
		return true;
	default:
		assert(0);
		return false;
	}
	
}


EXPORT_FUNC thread_id thread_current() {
	return GetCurrentThreadId();
}


EXPORT_FUNC void thread_yield() {
	Sleep(0);
}

EXPORT_FUNC void thread_sleep( const struct th_join_sec* sec ) {
	DWORD dwMSec;
	if( sec == NULL )
		thread_yield();
	dwMSec = sec->micro_sec/1000u + sec->sec*1000u;
	Sleep(dwMSec);
}

EXPORT_FUNC void thread_fastlock_create( fastlock* fl ) {
	BOOL ret = InitializeCriticalSectionAndSpinCount( fl , 2000 );
	assert( ret == TRUE );
}

EXPORT_FUNC void thread_fastlock_destroy( fastlock* fl ) {
	DeleteCriticalSection(fl);
}

EXPORT_FUNC bool thread_fastlock_require( fastlock* fl , const struct th_join_sec* ti ) {
	BOOL ret;
	size_t beg,end;
	size_t times=0;
	DWORD dwMSec;
	if( ti == NULL ) {
		EnterCriticalSection(fl);
	} else {
		if( ti->sec == 0 && ti->micro_sec == 0 ) {
			ret = TryEnterCriticalSection(fl);
			return ret == TRUE;
		} else {
			dwMSec = ti->micro_sec/1000u + ti->sec*1000;
			beg = (size_t)clock();
			times = 0;
			while( true ) {
				ret = TryEnterCriticalSection(fl);
				if( ret == TRUE ) {
					return true;
				} 
				end = (size_t)clock();
				if( end-beg >= dwMSec ) {
					return false;
				}
				/*
				 * Sleep for a while to wait for the lock
				 */
				++times;
				if( times > WIN32_SPIN_TO_SLEEP_LOOPS ) {
					Sleep(0);
					times = 0;
				}
			}
		}
	}
	return false;
}


EXPORT_FUNC void thread_fastlock_release( fastlock* fl ) {
	LeaveCriticalSection(fl);
}

#define SPLK_UNLOCK (LONG)0
#define SPLK_LOCK (LONG)1

EXPORT_FUNC void thread_spinlock_create( spinlock* sp ) {
#ifdef WIN32_ENABLE_CRITICAL_SECTION_AS_SPINLOCK
	thread_fastlock_create(sp);
#else
	*sp = (SPLK_UNLOCK);
#endif
}

EXPORT_FUNC void thread_spinlock_destroy( spinlock* sp ) {
#ifdef WIN32_ENABLE_CRITICAL_SECTION_AS_SPINLOCK
	thread_fastlock_destroy(sp);
#else
	assert(*sp == SPLK_UNLOCK);
#endif
}

EXPORT_FUNC bool thread_spinlock_require( spinlock* sp , bool once ) {
#ifdef WIN32_ENABLE_CRITICAL_SECTION_AS_SPINLOCK
	struct th_join_sec tjs;
	MAKE_TH_JOIN_SEC_ZERO(tjs);
	thread_fastlock_require( sp , once ? &tjs : NULL );
#else
	LONG ret;
	size_t times;
	size_t inc = 1;
	if( once ) {
		return InterlockedCompareExchange( (LONG volatile*)sp , SPLK_LOCK , SPLK_UNLOCK ) == SPLK_UNLOCK;
	} else {
		times = 0;
		while( true ) {
			ret = InterlockedCompareExchange( (LONG volatile*)sp , SPLK_LOCK , SPLK_UNLOCK );
			if( ret == SPLK_UNLOCK ) {
				return true;
			} else {
				times += inc;
				if( times > WIN32_PURE_SPINLOCK_LOOPS ) {
					Sleep(0);
					times = 0;
					++inc;
				}
			}
		}
	}
#endif
	
}

EXPORT_FUNC void thread_spinlock_release( spinlock* sp   ) {
#ifdef WIN32_ENABLE_CRITICAL_SECTION_AS_SPINLOCK
	thread_fastlock_release( sp );
#else
#ifndef NDEBUG
	assert( InterlockedCompareExchange( (LONG volatile*)sp, SPLK_UNLOCK , SPLK_LOCK ) == SPLK_LOCK );
#else
	InterlockedExchange((LONG volatile*)sp,SPLK_UNLOCK);
#endif
#endif
}

EXPORT_FUNC void thread_event_create( event* eve ) {
	InitializeConditionVariable(eve);
}

EXPORT_FUNC void thread_event_destroy( event* eve ) {
	/* Very interesting , the windows does not have a corresponding DestroyConditionVariable function */
}

EXPORT_FUNC bool thread_event_wait( event* eve , fastlock* fl , const struct th_join_sec* ti) {
	DWORD dwMSec ;
	BOOL ret;
	if(ti == NULL) {
		dwMSec = INFINITE;
	} else {
		if( ti->sec==0 && ti->micro_sec ==0 ) {
			dwMSec = 0;
		} else {
			dwMSec = ti->sec*1000u + ti->micro_sec/1000u;
		}
	}
	ret = SleepConditionVariableCS( eve , fl , dwMSec );
	return ret == TRUE;
}

EXPORT_FUNC void thread_event_signal( event* eve ) {
	WakeConditionVariable(eve);
}

EXPORT_FUNC bool thread_event_broadcast( event* eve ) {
	WakeAllConditionVariable( eve );
	return true;
}

EXPORT_FUNC void thread_tls_create( tls* t ) {
	*t = TlsAlloc();
	assert( *t != TLS_OUT_OF_INDEXES );
}

EXPORT_FUNC void thread_tls_destroy( tls t ) {
	BOOL ret = TlsFree(t);
	assert( ret == TRUE );
}

EXPORT_FUNC void thread_tls_set( tls t , void* var ) {
	BOOL ret = TlsSetValue( t , var );
	assert( ret == TRUE );
}

EXPORT_FUNC void thread_tls_get( tls t , void** rvar ) {
	*rvar = TlsGetValue(t);
}

#undef WIN32_THREAD_EXIT_CODE
#undef WIN32_SPIN_TO_SLEEP_LOOPS
#undef WIN32_PURE_SPINLOCK_LOOPS
C_CODE_END
#endif //_WIN32